#include "vl_file.h"
#include "exception/vl_exception.h"

using namespace VL;

#define VL_MAX_PATH 2048

/**
 * @Author: CimZzz
 * @Date: 2022-02-11 16:29:16
 * 
 * 构造函数, 当前所在目录
 */
VLFile::VLFile() {
	this->absolutePath = "";
	this->fileName = "";
	this->baseName = "";
	this->extName = "";
	this->aliasPath = "";
	this->actionDone = false;
}


/**
 * @Author: CimZzz
 * @Date: 2022-02-14 11:23:46
 * 
 * 构造函数, 指定文件夹下的路径
 * 
 * @param parent 父文件夹
 * @param path 基于父文件夹下的路径
 */
VLFile::VLFile(const VLFile& parent, std::string path) {
	if(parent.absolutePath.at(parent.absolutePath.size() - 1) == FILE_SEPERATOR) {
		this->initFileInfos(parent.absolutePath + path);
	} else {
		this->initFileInfos(parent.absolutePath + FILE_SEPERATOR + path);
	}
	this->aliasPath = path;
}

/**
 * @Author: CimZzz
 * @Date: 2022-02-14 11:23:46
 * 
 * 构造函数, 指定文件夹下的路径
 * 
 * @param parent 父文件夹
 * @param path 基于父文件夹下的路径
 */
VLFile::VLFile(VLFile* parent, std::string path) {
	if(parent->absolutePath.at(parent->absolutePath.size() - 1) == FILE_SEPERATOR) {
		this->initFileInfos(parent->absolutePath + path);
	} else {
		this->initFileInfos(parent->absolutePath + FILE_SEPERATOR + path);
	}
	this->aliasPath = path;
}


/**
 * @Author: CimZzz
 * @Date: 2022-02-14 11:23:46
 * 
 * 构造函数, 指定文件夹下的路径, 并替换后缀名
 * 
 * @param parent 父文件夹
 * @param path 基于父文件夹下的路径
 * @param ext 后缀名
 */
VLFile::VLFile(const VLFile& parent, std::string path, std::string ext) {
	int idx = path.find_last_of(".");
	if(idx != -1) {
		path = path.erase(idx) + ext;
	}

	if(parent.absolutePath.at(parent.absolutePath.size() - 1) == FILE_SEPERATOR) {
		this->initFileInfos(parent.absolutePath + path);
	} else {
		this->initFileInfos(parent.absolutePath + FILE_SEPERATOR + path);
	}
	this->aliasPath = path;
}


/**
 * @Author: CimZzz
 * @Date: 2022-02-11 16:31:04
 * 
 * 析构函数
 */
VLFile::~VLFile() {

}

/**
 * @Author: CimZzz
 * @Date: 2022-02-14 11:33:10
 * 
 * 初始化文件信息
 * @param absolutePath 文件的绝对路径
 */
void VLFile::initFileInfos(const std::string& absolutePath) {
	this->absolutePath = absolutePath;
	this->fileName = "";
	this->baseName = "";
	this->extName = "";
	this->actionDone = false;
	size_t seperatorPos, offset = this->absolutePath.length();
	while((seperatorPos = this->absolutePath.find_last_of(FILE_SEPERATOR, offset)) != -1) {
		if(seperatorPos == this->absolutePath.length() - 1) {
			offset--;
			if(offset == 0) {
				break;
			}
			continue;
		}
		this->fileName = this->absolutePath.substr(seperatorPos + 1, offset);
		int idx = this->fileName.find_last_of('.');
		if(idx == -1) {
			this->baseName = this->fileName;
		} else {
			this->baseName = this->fileName.substr(0, idx);
			this->extName = this->fileName.substr(idx);
		}
		break;
	}
}

/**
 * @Author: CimZzz
 * @Date: 2022-02-11 16:31:12
 * 
 * 获取当前文件的路径
 * 
 * @return 返回当前文件的绝对路径
 */
std::string VLFile::getAbsolutePath() const {
	return this->absolutePath;
}

/**
 * @Author: CimZzz
 * @Date: 2022-02-14 10:51:45
 * 
 * 获取当前文件名
 * @return 返回当前文件名
 */
std::string VLFile::getFileName() {
	return this->fileName;
}


/**
 * @Author: CimZzz
 * @Date: 2022-02-18 11:42:40
 * 
 * 获取文件无后缀名
 * @return 无后缀文件名
 */
std::string VLFile::getBaseName() {
	return this->baseName;
}

/**
 * @Author: CimZzz
 * @Date: 2022-02-18 11:43:07
 * 
 * 获取文件后缀名
 * @return 文件后缀名
 */
std::string VLFile::getExtName() {
	return this->extName;
}

/**
 * @Author: CimZzz
 * @Date: 2022-02-14 11:08:57
 * 
 * 重命名文件到指定路径上
 * @param filePath 文件路径
 * 
 * @return 重命名成功返回 true
 */
bool VLFile::rename(std::string filePath) {
	return ::rename(this->absolutePath.c_str(), filePath.c_str()) == 0;
}

/**
 * @Author: CimZzz
 * @Date: 2022-02-14 11:10:02
 * 
 * 复制文件到指定路径上
 * @param filePath 文件路径
 * 
 * @return 复制成功返回 true
 */
bool VLFile::copy(std::string filePath) {
	if(this->isExist()) {
		std::ifstream input(this->absolutePath);
		if(!input) {
			return false;
		}

		std::ofstream output(filePath);
		if(!output) {
			return false;
		}

		output << input.rdbuf();
		
		input.close();
		output.close();

		return !output.bad() && !output.fail();
	}
	return false;
}

/**
 * @Author: CimZzz
 * @Date: 2022-02-11 17:09:30
 * 
 * 判断文件是否存在
 * @return 如果存在返回 true
 */
bool VLFile::isExist() {
	bool isExist = false;
	#if defined _WIN32 || defined _WIN64
		#ifdef _WIN32
			isExist = _access(this->absolutePath.c_str(), 0) == 0;
		#else
			isExist = access(this->absolutePath.c_str(), 0) == 0;
		#endif	
	#else
		struct stat s;
		if(stat(this->absolutePath.c_str(), &s) == 0) {
			isExist = true;
		}
	#endif

	return isExist;
}

/**
 * @Author: CimZzz
 * @Date: 2022-02-11 17:02:06
 * 
 * 通过路径检查文件类型
 * @return 文件类型
 */
VLFile::FileType VLFile::getFileType() {
	FileType type = FT_Unknown;
	#if defined _WIN32 || defined _WIN64
		WIN32_FIND_DATAA fileInfo;
		if (::FindFirstFileA(this->absolutePath.c_str(), &fileInfo) != INVALID_HANDLE_VALUE) {
			if (fileInfo.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
				type |= FT_Dir;
			}
			else {
				type |= FT_File;
			}
		}
	#else
		struct stat s;
		if(lstat(this->absolutePath.c_str(), &s) == 0) {
			if(S_ISDIR(s.st_mode)) {
				type |= FT_Dir;
			}
			if(S_ISREG(s.st_mode)) {
				type |= FT_File;
			}
			if(S_ISLNK(s.st_mode)) {
				type |= FT_Link;
			}
		}
	#endif

	return type;
}

/**
 * @Author: CimZzz
 * @Date: 2022-02-11 17:14:25
 * 
 * 判断当前路径是否指向文件
 * 
 * @return 如果是文件返回 true
 */
bool VLFile::isFile() {
	return (VLFile::getFileType() & FT_File) != 0;
}

/**
 * @Author: CimZzz
 * @Date: 2022-02-11 17:15:15
 * 
 * 判断当前路径是否指向文件夹
 * 
 * @return 如果是文件夹返回 true
 */
bool VLFile::isDirectory() {
	return (VLFile::getFileType() & FT_Dir) != 0;
}


/**
 * @Author: CimZzz
 * @Date: 2022-02-11 17:27:16
 * 
 * 获取文件夹下全部文件
 * 
 * @param fileType 指定文件类型
 * 
 * @return 文件列表
 */
const std::vector<std::shared_ptr<VLFile>> VLFile::listFiles(FileType fileType) {
	std::vector<std::shared_ptr<VLFile>> fileList;

	if(this->isDirectory()) {
		#if defined _WIN32 || defined _WIN64
			intptr_t handle;
			_finddata_t findData;

			std::string dir = this->absolutePath + FILE_SEPERATOR + "*";
			handle = ::_findfirst(dir.c_str(), &findData);
			if (handle == -1) {
				return fileList;
			}
			do {
				if (strcmp(findData.name, ".") == 0 || strcmp(findData.name, "..") == 0) {
					continue;
				}

				std::string filename(findData.name);
				filename = this->absolutePath + FILE_SEPERATOR + filename;
				std::shared_ptr<VLFile> filePtr(new VLAbsolutePathFile(filename));
				if(fileType == FT_Unknown) {
					fileList.push_back(filePtr);
					continue;
				}

				FileType currentFileType = filePtr->getFileType();
				if(currentFileType & fileType) {
					fileList.push_back(filePtr);
				}
			} while (::_findnext(handle, &findData) == 0);

			::_findclose(handle);
		#else
			DIR *dir;
			struct dirent *diread;
			if((dir = opendir(this->absolutePath.c_str())) != nullptr) {
				while((diread = readdir(dir)) != nullptr) {
					if(strcmp(diread->d_name, ".") == 0 || strcmp(diread->d_name, "..") == 0) {
						continue;
					}
					std::string filename(diread->d_name);
					filename = this->absolutePath + FILE_SEPERATOR + filename;
					std::shared_ptr<VLFile> filePtr(new VLAbsolutePathFile(filename));
					if(fileType == FT_Unknown) {
						fileList.push_back(filePtr);
						continue;
					}

					FileType currentFileType = filePtr->getFileType();
					if(currentFileType & fileType) {
						fileList.push_back(filePtr);
					}
				}
			}
		#endif
	}


	return fileList;
}


/**
 * @Author: CimZzz
 * @Date: 2022-02-18 16:54:34
 * 
 * 获取父文件夹路径
 */
std::string VLFile::getParentPath() const & {
	size_t seperatorPos, offset = this->absolutePath.length();
	while((seperatorPos = this->absolutePath.find_last_of(FILE_SEPERATOR, offset)) != -1) {
		if(seperatorPos == this->absolutePath.length() - 1) {
			offset--;
			if(offset == 0) {
				break;
			}
			continue;
		}
		return this->absolutePath.substr(0, seperatorPos);
	}
	return "";
}

/**
 * @Author: CimZzz
 * @Date: 2022-02-14 11:36:52
 * 
 * 获取文件夹
 * @return 返回父级文件夹
 */
VLFile VLFile::getParent() const & {
	std::string parentPath = this->getParentPath();
	VLFile parentFile;
	parentFile.initFileInfos(parentPath);
	parentFile.aliasPath = parentPath;
	return parentFile;
}


/**
 * @Author: CimZzz
 * @Date: 2022-02-17 19:10:54
 * 
 * 更换后缀名
 * @param ext 后缀名
 * @return 更换过后缀名的文件
 */
VLFile VLFile::replaceExt(std::string ext) const & {
	return VLFile(getParent(), this->baseName + ext);
}


/**
 * @Author: CimZzz
 * @Date: 2022-02-21 17:07:34
 * 
 * 确保上级文件夹存在，如果不存在会尝试去创建新文件夹
 * @return 上级文件夹存在返回 true
 */
bool VLFile::makeSureParentDirExist() {
	VLFile parent = this->getParent();
	if(parent.absolutePath == "") {
		return true;
	}

	if(!parent.isExist()) {
		if(parent.makeSureParentDirExist()) {
			return parent.newDir();
		}
	}

	return true;
}

/**
 * @Author: CimZzz
 * @Date: 2022-02-15 16:00:19
 * 
 * 创建文件夹
 * 
 * @return 如果文件已存在并且文件类型不为文件夹, 或者创建失败返回 false
 */
bool VLFile::newDir() {
	this->actionDone = false;
	bool isExist = this->isExist();
	if(isExist && this->getFileType() == FT_Dir) {
		return true;
	}

	if(isExist) {
		return false;
	}
	
	#if defined _WIN32 || defined _WIN64
		return mkdir(this->absolutePath.c_str()) == 0;
	#else
		return mkdir(this->absolutePath.c_str(), 0775) == 0;
	this->actionDone = true;
	#endif
}

/**
 * @Author: CimZzz
 * @Date: 2022-02-15 16:00:19
 * 
 * 创建文件
 * 
 * @return 如果文件已存在并且文件类型不为文件, 或者创建失败返回 false
 */
bool VLFile::newFile() {
	this->actionDone = false;
	bool isExist = this->isExist();
	if(isExist && this->getFileType() == FT_File) {
		return true;
	}

	if(isExist) {
		return false;
	}

	std::ofstream output;
	output.open(this->absolutePath);
	if(output.bad()) {
		return false;
	}
	output.close();
	this->actionDone = true;
	return !output.fail();
}


/**
 * @Author: CimZzz
 * @Date: 2022-02-16 16:01:41
 * 
 * 删除文件
 * 
 * @return 删除成功返回 true
 */
bool VLFile::deleteFile() {
	this->actionDone = false;
	if(!this->isExist()) {
		return true;
	}
	this->actionDone = true;
	if(this->isFile()) {
		return std::remove(this->absolutePath.c_str()) == 0;
	}

	auto listChildren = this->listFiles();
	if(listChildren.empty()) {
		return std::remove(this->absolutePath.c_str()) == 0;
	}

	for(auto iter = listChildren.begin() ; iter != listChildren.end() ; iter ++) {
		if(!(*iter)->deleteFile()) {
			return false;
		}
	}

	return std::remove(this->absolutePath.c_str()) == 0;
}



/**
 * @Author: CimZzz
 * @Date: 2022-02-16 16:53:16
 * 
 * 返回是否实际进行了创建/删除操作
 * 
 * @return 如果实际进行了上述操作返回 true
 */
bool VLFile::isActionDone() {
	return this->actionDone;
}


/**
 * @Author: CimZzz
 * @Date: 2022-02-17 17:00:41
 * 
 * 获取文件最后的修改时间
 * 
 * @return 修改时间
 */
int64_t VLFile::getModifyTime() {
	struct stat result;

    if(stat(this->absolutePath.c_str(), &result)==0) {
		#if defined __linux__
			auto mod_time = result.st_mtim.tv_sec * 1000 + result.st_mtim.tv_nsec / 1000;
		#elif defined __APPLE__ || defined __MACH__
        	auto mod_time = result.st_mtimespec.tv_sec * 1000 + result.st_mtimespec.tv_nsec / 1000;
		#else
        	auto mod_time = result.st_mtimespec.tv_sec * 1000 + result.st_mtimespec.tv_nsec / 1000;
		#endif
        return mod_time;
    }

	return -1;
}



/**
 * @Author: CimZzz
 * @Date: 2022-03-29 11:04:39
 * 
 * 获取别名路径
 * 
 * @return 别名路径
 */
std::string VLFile::getAliasPath() {
	return this->aliasPath;
}

/**
 * @Author: CimZzz
 * @Date: 2022-02-11 19:44:43
 * 
 * 获取用户根目录
 * 
 * @return 返回用户根目录
 */
VLFile VLFile::getUserProfileDir() {
	VLFile file;
	#if defined _WIN32 || defined _WIN64
		char pathBuf[VL_MAX_PATH] = { 0 };
		unsigned int size = ::GetEnvironmentVariableA("USERPROFILE", pathBuf, VL_MAX_PATH);
		
		file.absolutePath = std::string(pathBuf); 
	#else
		file.absolutePath = std::string(std::getenv("HOME"));
	#endif

	return file;
}


/**
 * @Author: CimZzz
 * @Date: 2022-02-11 16:30:01
 * 
 * 构造函数, 指定文件路径
 * 
 * @param path 文件路径
 */
VLRealPathFile::VLRealPathFile(std::string path) {
	char tempPath[VL_MAX_PATH];
	bool isResolveSuccess = false;

	#if defined _WIN32 || defined _WIN64
		_fullpath(tempPath, relative.c_str(), VL_MAX_PATH);
	#else
		isResolveSuccess = realpath(path.c_str(), tempPath) != nullptr;
	#endif

	if(!isResolveSuccess) {
		throw VLException("File", "file not exist: %s", path.c_str());
	}
	std::string realPath(tempPath);
	this->initFileInfos(realPath);
	this->aliasPath = path;
}


/**
 * @Author: CimZzz
 * @Date: 2022-02-14 11:23:46
 * 
 * 构造函数, 指定文件夹下的路径
 * 
 * @param parent 父文件夹
 * @param path 基于父文件夹下的路径
 */
VLRealPathFile::VLRealPathFile(const VLFile& parent, std::string path) {
	std::string parentPath = parent.getAbsolutePath();
	std::string absolutePath;
	if(parentPath.at(parentPath.size() - 1) == FILE_SEPERATOR) {
		absolutePath = parentPath + path;
	} else {
		absolutePath = parentPath + FILE_SEPERATOR + path;
	}

	char tempPath[VL_MAX_PATH];
	bool isResolveSuccess = false;

	#if defined _WIN32 || defined _WIN64
		_fullpath(tempPath, absolutePath.c_str(), VL_MAX_PATH);
	#else
		isResolveSuccess = realpath(absolutePath.c_str(), tempPath) != nullptr;
	#endif

	if(!isResolveSuccess) {
		throw VLException("File", "file not exist: %s", absolutePath.c_str());
	}
	std::string realPath(tempPath);
	this->initFileInfos(realPath);
	this->aliasPath = path;
}


/**
 * @Author: CimZzz
 * @Date: 2022-02-11 16:30:01
 * 
 * 构造函数, 指定文件路径
 * 
 * @param path 文件路径
 */
VLAbsolutePathFile::VLAbsolutePathFile(std::string path) {
	this->initFileInfos(path);
	this->aliasPath = path;
}
